﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace LTorrent.Torrent.File
{
    #region BElement
    /// <summary>
    /// Universal bencode store
    /// </summary>
    public class BElement
    {
        
        public BItem STDItem { get; private set; }
        public BList LSTItem { get; private set; }
        public BDictionary DICItem { get; private set; }

        public BElement()
        {
            DICItem = null; LSTItem = null; STDItem = null;
        }

        public string ToString()
        {
            if (STDItem != null)
                return STDItem.ToString();
            else return "null";
        }
        /// <summary>
        /// Создание и чтение простого типа данных string\integer
        /// </summary>
        /// <param name="Reader">поток чтения</param>
        /// <param name="CurrentCode">Считанный заранее символ</param>
        public void DeserializeToBItem(BinaryReader Reader, char CurrentCode)
        {
            char C;
            if (CurrentCode == 'i')
            {//считывание числа
                string Value = "";
                C = (char)Reader.PeekChar(); Reader.ReadChar();
                while (C != 'e')
                {//конец
                    Value += C;
                    C = (char)Reader.PeekChar(); Reader.ReadChar();
                }
                try
                {
                    int Res = Int32.Parse(Value);
                    STDItem = new BItem(Res);
                }
                catch (Exception ex)
                {
                    //Здесь можно вызвать throw ошибку. А можно сделать объект null'ом
                    STDItem = null;
                }
                return;
            }
            int length = (int)CurrentCode - (int)'0';
            C = (char)Reader.PeekChar(); Reader.ReadChar();
            while (C != ':' && (C >= '0' && C <= '9'))
            {
                length = length * 10 + (int)C - (int)'0';
                C = (char)Reader.PeekChar(); Reader.ReadChar();
            }
            if (C != ':')
            {//Можно вызвать ошибку (так же как выше, просто обнулим объект, вместо throw new Exception("Неверно задан объект");
                // так как второй способ throw требует обработки выше по коду... а пока пишем только класс =)
                STDItem = null;
                return;
            }
            byte[] value;

            
            value = Reader.ReadBytes(length);
            


            STDItem = new BItem(value);

        }
        /// <summary>
        /// список. Считаем, что l была считана
        /// </summary>
        /// <param name="Reader">ридер файла</param>
        public void DeserializeToBList(BinaryReader Reader)
        {
            LSTItem = new BList();
            BElement Temp = Deserialize(Reader);
            while (Temp != null)
            {
                LSTItem.Add(Temp);
                Temp = Deserialize(Reader);
            }
            if (LSTItem.Count == 0) LSTItem = null;//опять же - здесь можно генерировать ошибку о неверной структуре файла.
        }
        /// <summary>
        /// Считывание словаря
        /// </summary>
        /// <param name="Reader">поток чтения файла</param>
        public void DeserializeToBDic(BinaryReader Reader)
        {
            DICItem = new BDictionary();
            BElement FirstTemp = Deserialize(Reader);
            BElement SecondTemp = Deserialize(Reader);
            while (FirstTemp != null && SecondTemp != null)
            {
                DICItem.Add(FirstTemp, SecondTemp);
                FirstTemp = Deserialize(Reader);
                if (FirstTemp != null)
                    SecondTemp = Deserialize(Reader);
            }
            if (DICItem.Count == 0) DICItem = null;//Либо писать об ошибке в структуре файла
        }

        /// <summary>
        /// Определяем тип следующего элемента. Запускаем создание
        /// </summary>
        /// <param name="Reader">поток чтения</param>
        /// <returns>Новый элемент</returns>
        public static BElement Deserialize(BinaryReader Reader)
        {
            char C = (char)Reader.PeekChar(); Reader.ReadChar();
            switch (C)
            {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case 'i':
                    {//простой тип данных
                        BElement STDElement = new BElement();
                        STDElement.DeserializeToBItem(Reader, C);
                        return STDElement;
                    }
                case 'l':
                    {//список
                        BElement LSTElement = new BElement();
                        LSTElement.DeserializeToBList(Reader);
                        return LSTElement;
                    }

                case 'd':
                    {//словарь
                        BElement DICElement = new BElement();
                        DICElement.DeserializeToBDic(Reader);
                        return DICElement;
                    }
                default://("e")
                    return null;
            }
        }
        public void Serialize(BinaryWriter outStream)
        {
            if (STDItem != null)
                STDItem.Serialize(outStream);
            else
                if (LSTItem != null)
                    LSTItem.Serialize(outStream);
                else
                    DICItem.Serialize(outStream);
        }
    }
    #endregion
}
